/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.patterns.ColSubtype;
import ro.dta.idbi.ui.sql.SqlColumnCell;
import ro.dta.idbi.ui.sql.SqlForeignKeyCell;
import ro.dta.idbi.ui.sql.SqlGraph;
import ro.dta.idbi.ui.sql.SqlTableCell;
import uk.ac.ic.doc.automed.reps.Schema;

import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;

/**
 * UI for the column to subtype transformation pattern
 * 
 * @author Tudor Dobrila
 * 
 */
public class ColSubtypeDialog extends AbstractPatternDialog {

	private static final long serialVersionUID = 7765960537120509172L;

	private final static Logger LOGGER = Logger
			.getLogger("com.dta.idbi.patterns.ui.ColSubtypeDialog");
	
	/**
	 * Check if the current pattern can be applied over a given set of objects
	 * 
	 * Used to check if the items selected by the user in the UI can be used to
	 * apply this transformation pattern over them
	 * 
	 * @param schema
	 *            Schema in the context of which to check the validity of the
	 *            array of objects
	 * @param objects
	 *            Set of objects
	 * @return True if pattern can be applied over set of objects, false
	 *         otherwise
	 */
	public static boolean isValid(final Schema schema, final Object[] objects) {
		boolean result;
		if (objects.length == 1 && objects[0] instanceof ISqlColumn) {
			final ISqlColumn col = (ISqlColumn) objects[0];
			result = col.isNullable() && !col.isPK();
		} else {
			result = false;
		}
		return result;
	}

	/**
	 * Name of the parent table of the column being transformed
	 */
	private final transient String parentName;

	private transient SqlForeignKeyCell foreignKey;

	/**
	 * Column to subtype dialog class constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of this dialog
	 * @param schema
	 *            Schema over which the transformation is applied
	 * @param element
	 *            Sql construct(s) over which the transformation is applied
	 * @param beforeLabel
	 *            Label for the before diagram
	 * @param afterLabel
	 *            Label for the transformed diagram
	 */
	public ColSubtypeDialog(final IMainFrame parentFrame, final Schema schema,
			final ISqlConstruct[] element, final JLabel beforeLabel, final JLabel afterLabel) {
		super(parentFrame, schema, element, beforeLabel, afterLabel);

		if (!isValid(schema, element)) {
			throw new IllegalArgumentException(
					"This pattern can't be applied to the given elements");
		}

		final SqlTableCell parent = (SqlTableCell) ((SqlColumnCell) element[0]).getParent();
		parentName = parent.getValue().toString();
		updateMacro("newTable");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.ui.patterns.TransformationDialog#createOriginalDiagram()
	 */
	@Override
	protected mxGraphComponent createOriginalDiagram() {
		final SqlGraph graph = new SqlGraph();
		graph.getModel().beginUpdate();

		try {
			final SqlTableCell parentTable = (SqlTableCell) ((SqlColumnCell) element[0])
					.getParent();
			final SqlTableCell newTable = (SqlTableCell) parentTable.clone();
			final ISqlColumn col = newTable.getColumn(element[0].getValue().toString());
			col.setStyle("SEL_COL");

			final mxGeometry newGeometry = new mxGeometry(5, 5,
					element[0].getGeometry().getWidth(), element[0].getGeometry().getHeight());
			newTable.setGeometry(newGeometry);
			newTable.layout(graph);
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return new mxGraphComponent(graph);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.ui.patterns.TransformationDialog#createTransformedDiagram()
	 */
	@Override
	protected JComponent createTransformedDiagram() {
		JPanel diagPanel = new JPanel(new BorderLayout());
		
		ISqlTable newTable = null;
		final SqlGraph graph = new EditableGraph();
		graph.getModel().beginUpdate();

		ISqlTable parentTable = null;
		try {
			SqlTableCell origTable = null;

			// Create original table
			parentTable = (SqlTableCell) ((SqlColumnCell) element[0])
					.getParent();
			origTable = (SqlTableCell) parentTable.clone();
			final ISqlColumn col = origTable.getColumn(element[0].getValue().toString());
			origTable.removeColumn(col);
			final mxGeometry newGeometry = new mxGeometry(5, 5,
					element[0].getGeometry().getWidth(), element[0].getGeometry().getHeight());
			origTable.setGeometry(newGeometry);
			origTable.relayout(graph);

			// Create new table
			final mxGeometry geom = new mxGeometry(200, 5, element[0].getGeometry().getWidth(),
					element[0].getGeometry().getHeight());
			newTable = new SqlTableCell(null, null, "", "newTable", geom, "SEL_TABLE");

			// Add PK columns to the new table
			for (ISqlColumn c : origTable.getColumns()) {
				if (c.getStyle().equals("PRIMARYKEY")) {
					final ISqlColumn newCol = (ISqlColumn) c.clone();
					newTable.addColumn(newCol);
				}
			}
			final ISqlColumn col2 = (ISqlColumn) col.clone();
			col2.setStyle("COLUMN");
			col2.setNullable(false);
			col2.setValue(col2.getValue().toString().replace("?", ""));
			newTable.addColumn(col2);
			newTable.relayout(graph);

			final String fkName = "newTable_isa_" + parentName + "_fk";
			foreignKey = new SqlForeignKeyCell(null, null, "", fkName, newTable, origTable, "FK_EDGE");
			graph.addCell(foreignKey, graph.getDefaultParent());
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			final mxHierarchicalLayout layout = new mxHierarchicalLayout(graph);
			layout.execute(graph.getDefaultParent());
			graph.getModel().endUpdate();
		}

		final mxGraphComponent graphComponent = new mxGraphComponent(graph);
		graphComponent.setEnterStopsCellEditing(true);
		graphComponent.addListener(mxEvent.LABEL_CHANGED, new mxIEventListener() {

			@Override
			public void invoke(final Object arg0, final mxEventObject arg1) {
				final SqlTableCell table = (SqlTableCell) arg1.getProperty("cell");
				updateMacro(table.getValue().toString());
				final String fkName = table.getValue() + "_isa_" + parentName + "_fk";
				foreignKey.setValue(fkName);

				graphComponent.revalidate();
				graphComponent.repaint();
			}
		});

		if (newTable != null) {
			graphComponent.startEditingAtCell(newTable);
			
			// Set the child tables
			List<ISqlTable> childTbls = parentTable.getChildren();
			if (childTbls != null && childTbls.size() > 0) {
				diagPanel.add(graphComponent, BorderLayout.CENTER);

				JPanel bottomPanel = new JPanel(new FlowLayout());
				bottomPanel.add(new JLabel("Children of " + parentName + ": "));

				final JComboBox<Object> childCombo = new JComboBox<Object>(parentTable.getChildren().toArray());
				bottomPanel.add(childCombo);

				final ISqlTable newTable1 = newTable;
				JButton childBtn = new JButton("Set name of new table");
				childBtn.addActionListener(new ActionListener() {

					@Override
					public void actionPerformed(ActionEvent paramActionEvent) {
						graphComponent.stopEditing(true);
						graph.getModel().beginUpdate();
						graph.getModel().setValue(newTable1, childCombo.getSelectedItem());
						graph.getModel().endUpdate();
						updateMacro(childCombo.getSelectedItem().toString());						
					}
				});
				bottomPanel.add(childBtn);

				diagPanel.add(bottomPanel, BorderLayout.SOUTH);
			}
		}
		
		diagPanel.add(graphComponent, BorderLayout.CENTER);

		return diagPanel;
	}

	/**
	 * Update the macro command
	 * 
	 * @param name
	 *            Name of the new table being created
	 */
	private void updateMacro(final String name) {
		String validName = element[0].getValue().toString().replace("?", "");
		macroField.setText(ColSubtype.COMMAND + " (<<" + parentName + ","
				+ validName + ">>, <<" + name + ">>)");
	}
}
